package com.jzo2o.health.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jzo2o.api.trade.NativePayApi;
import com.jzo2o.api.trade.TradingApi;
import com.jzo2o.api.trade.dto.request.NativePayReqDTO;
import com.jzo2o.api.trade.dto.response.NativePayResDTO;
import com.jzo2o.api.trade.dto.response.TradingResDTO;
import com.jzo2o.api.trade.enums.PayChannelEnum;
import com.jzo2o.api.trade.enums.TradingStateEnum;
import com.jzo2o.common.expcetions.BadRequestException;
import com.jzo2o.common.expcetions.CommonException;
import com.jzo2o.common.expcetions.DBException;
import com.jzo2o.common.model.msg.TradeStatusMsg;
import com.jzo2o.common.utils.DateUtils;
import com.jzo2o.common.utils.ObjectUtils;
import com.jzo2o.health.enums.OrderPayStatusEnum;
import com.jzo2o.health.enums.OrderStatusEnum;
import com.jzo2o.health.mapper.OrdersMapper;
import com.jzo2o.health.mapper.ReservationSettingMapper;
import com.jzo2o.health.model.UserThreadLocal;
import com.jzo2o.health.model.domain.*;
import com.jzo2o.health.model.dto.request.PlaceOrderReqDTO;
import com.jzo2o.health.model.dto.response.OrdersPayResDTO;
import com.jzo2o.health.model.dto.response.PlaceOrderResDTO;
import com.jzo2o.health.properties.TradeProperties;
import com.jzo2o.health.service.*;
import org.aspectj.weaver.ast.Or;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;

@Service
public class IOrdersCreateServiceImpl extends ServiceImpl<OrdersMapper, Orders> implements IOrdersCreateService {
    @Resource
    private IReservationSettingService reservationSettingService;
    @Resource
    private ISetmealService setmealService;
    @Resource
    private IMemberService memberService;
    @Resource
    private ReservationSettingMapper reservationSettingMapper;
    @Resource
    private TradeProperties tradeProperties;
    @Resource
    private NativePayApi nativePayApi;
    @Resource
    private TradingApi tradingApi;
    @Resource
    private IOrdersCreateService ordersCreateService;
    @Override
    public PlaceOrderResDTO placeOrder(PlaceOrderReqDTO dto) {
        //是否还有预约名额
        ReservationSetting reservationSetting = reservationSettingService.lambdaQuery()
                .eq(ReservationSetting::getOrderDate, dto.getReservationDate())
                .one();
        if (reservationSetting == null) {
            throw new CommonException("暂未开通" + dto.getReservationDate() + "的预约服务");
        }
        if (reservationSetting.getReservations() >= reservationSetting.getNumber()) {
            throw new CommonException("预约已达上线"); //todo 这里不能抛异常
        }

        //根据套餐id去查询套餐信息
        Setmeal setmeal = setmealService.lambdaQuery()
                .eq(Setmeal::getId, dto.getSetmealId())
                .one();

        //套餐试用性别要匹配

        if (!ObjectUtils.equals(dto.getCheckupPersonSex(),setmeal.getSex()) && setmeal.getSex() != 0) {
            throw new BadRequestException("套餐适用性别不符");
        }

        //保存到Orders表里
        Orders orders = BeanUtil.toBean(dto, Orders.class);
        orders.setOrderStatus(OrderStatusEnum.NO_PAY.getStatus());
        orders.setPayStatus(OrderPayStatusEnum.NO_PAY.getStatus());
        orders.setSetmealId(setmeal.getId());
        orders.setSetmealName(setmeal.getName());
        orders.setSetmealSex(setmeal.getSex());
        orders.setSetmealAge(setmeal.getAge());
        orders.setSetmealImg(setmeal.getImg());
        orders.setSetmealRemark(setmeal.getRemark());
        orders.setSetmealPrice(BigDecimal.valueOf(setmeal.getPrice()));
        Long userId = UserThreadLocal.currentUserId();
        orders.setMemberId(userId);
        //查询用户手机号码
        Member member = memberService.lambdaQuery().eq(Member::getId, userId).one();
        orders.setMemberPhone(member.getPhone());
        orders.setCreateTime(LocalDateTime.now());
        orders.setSortBy(DateUtils.toEpochMilli(LocalDateTime.now()));

        boolean saved = save(orders);
        if (!saved) {
            throw new CommonException("下单失败");
        }

        //根据order_date修改reservation_setting表
        reservationSettingMapper.updateReservations(reservationSetting.getId());

        //构建返回值
        PlaceOrderResDTO placeOrderResDTO = new PlaceOrderResDTO();

        placeOrderResDTO.setId(orders.getId());
        return placeOrderResDTO;
    }

    @Override
    public OrdersPayResDTO pay(Long id, PayChannelEnum tradingChannel) {
        //1. 根据id查询订单
        Orders orders = getById(id);
        if (orders == null) {
            throw new CommonException("订单不存在");
        }
        //订单如果是已支付状态,且交易单号不为空,说明是已支付状态, 直接返回结果
        if (ObjectUtils.equals(orders.getPayStatus(),OrderPayStatusEnum.PAY_SUCCESS.getStatus())
        && orders.getTradingOrderNo() != null) {
            OrdersPayResDTO res = BeanUtil.toBean(orders, OrdersPayResDTO.class);
            return res;
        }
        //2. 远程调用trade支付服务申请支付, 获取二维码
        NativePayReqDTO nativePayReqDTO = new NativePayReqDTO();
        nativePayReqDTO.setProductOrderNo(orders.getId());
        //判断业务渠道有没有修改
        nativePayReqDTO.setChangeChannel(!ObjectUtils.equals(orders.getTradingChannel(),tradingChannel));
        nativePayReqDTO.setTradingChannel(tradingChannel);
        nativePayReqDTO.setProductAppId("jzo2o.health");
        nativePayReqDTO.setMemo(orders.getSetmealName());
        nativePayReqDTO.setEnterpriseId(ObjectUtils.equals(PayChannelEnum.ALI_PAY,tradingChannel)?
                tradeProperties.getAliEnterpriseId():tradeProperties.getWechatEnterpriseId());
        //查询价格
        Setmeal setmeal = setmealService.lambdaQuery().eq(Setmeal::getId, orders.getSetmealId()).one();
        //防止丢失精度 float转为bigDecimal时可能会丢失精度
        BigDecimal bd = new BigDecimal(String.valueOf(setmeal.getPrice())).setScale(2, RoundingMode.HALF_UP);
        nativePayReqDTO.setTradingAmount(bd);

        //远程调用
        NativePayResDTO downLineTrading = nativePayApi.createDownLineTrading(nativePayReqDTO);
        if (downLineTrading == null) {
            throw new CommonException("订单" + id + "申请支付失败");
        }
        //4. 把结果里支付渠道和交易单号更新到orders表里
        boolean updated = lambdaUpdate()
                .eq(Orders::getId, id)
                .set(Orders::getTradingChannel, downLineTrading.getTradingChannel())
                .set(Orders::getTradingOrderNo, downLineTrading.getTradingOrderNo())
                .update();
        if (!updated) {
            throw new CommonException("订单" + id + "更新交易单号失败");
        }
        //5. 返回数据
        OrdersPayResDTO ordersPayResDTO = new OrdersPayResDTO();
        ordersPayResDTO.setQrCode(downLineTrading.getQrCode());
        ordersPayResDTO.setProductOrderNo(id);
        ordersPayResDTO.setTradingOrderNo(downLineTrading.getTradingOrderNo());
        ordersPayResDTO.setTradingChannel(downLineTrading.getTradingChannel());
        return ordersPayResDTO;
    }

    @Override
    public OrdersPayResDTO payResult(Long id) {
        //1. 查订单
        Orders order = getById(id);
        if (order == null) {
            throw new BadRequestException("订单不存在");
        }
        //2. 如果订单状态不为已支付, 就需要远程调用trade服务来查询新的订单状态, 如果是已支付, 就更新订单的状态
        if (ObjectUtils.equals(order.getOrderStatus(),OrderStatusEnum.NO_PAY.getStatus())) {
            //远程调用trade服务查询最新服务
            TradingResDTO tradingResult = tradingApi.findTradResultByTradingOrderNo(order.getTradingOrderNo());
            if (ObjectUtils.equals(tradingResult.getTradingState(),TradingStateEnum.YJS)) {
                //如果是已支付, 需要功能新订单状态
                TradeStatusMsg tradeStatusMsg = new TradeStatusMsg();
                tradeStatusMsg.setTradingOrderNo(tradingResult.getTradingOrderNo());
                tradeStatusMsg.setTransactionId(tradingResult.getTransactionId());
                tradeStatusMsg.setProductOrderNo(id);
                ordersCreateService.paySuccess(tradeStatusMsg);

                //构建返回值
                OrdersPayResDTO ordersPayResDTO = new OrdersPayResDTO();
                ordersPayResDTO.setPayStatus(OrderPayStatusEnum.PAY_SUCCESS.getStatus());
                ordersPayResDTO.setProductOrderNo(id);
                ordersPayResDTO.setTradingOrderNo(tradingResult.getTradingOrderNo());
                ordersPayResDTO.setTradingChannel(tradingResult.getTradingChannel());
                return ordersPayResDTO;
            }
        }
        OrdersPayResDTO ordersPayResDTO = new OrdersPayResDTO();
        ordersPayResDTO.setPayStatus(order.getPayStatus());
        ordersPayResDTO.setProductOrderNo(id);
        ordersPayResDTO.setTradingOrderNo(order.getTradingOrderNo());
        ordersPayResDTO.setTradingChannel(order.getTradingChannel());
        return ordersPayResDTO;
    }

    @Override
    @Transactional
    public void paySuccess(TradeStatusMsg tradeStatusMsg) {
        boolean updated = lambdaUpdate()
                .eq(Orders::getId, tradeStatusMsg.getProductOrderNo())
                .set(Orders::getOrderStatus, OrderStatusEnum.WAITING_CHECKUP.getStatus())
                .set(Orders::getPayStatus, OrderPayStatusEnum.PAY_SUCCESS.getStatus())
                .set(Orders::getPayTime, LocalDateTime.now())
                .set(Orders::getTradingOrderNo, tradeStatusMsg.getProductOrderNo())
                .set(Orders::getTransactionId, tradeStatusMsg.getTransactionId())
                .update();
        if (!updated) {
            throw new DBException("更新订单" + tradeStatusMsg.getProductOrderNo() + "支付状态异常");
        }

    }
}
